/*---------------------------------------------------------------------------*\
**
**   							Heroes IV
**   				  Copyright 2000, The 3DO Company
**
** 						       army_dialog_base.cpp
**
**   $Header: /game/army_dialog_base.cpp $
**
**	$NoKeywords: $
**
\*---------------------------------------------------------------------------*/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "army_dialog_base.h"

#include "abbreviate_number.h"
#include "abstract_town.h"
#include "adaptor_handler.h"
#include "adv_artifact_pile.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_map_window.h"
#include "adaptor_handler.h"
#include "army.h"
#include "army_list.h"
#include "artifact_icon.h"
#include "artifact_keyword.h"
#include "artifact_properties.h"
#include "artifact_type.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer_cache_window.h"
#include "bound_handler.h"
#include "button_cache.h"
#include "cached_grail_data_source.h"
#include "cheats.h"
#include "combat_model_window.h"
#include "creature.h"
#include "creature_array_window.h"
#include "creature_traits.h"
#include "drag_artifact.h"
#include "drag_creature.h"
#include "enum_operations.h"
#include "external_string.h"
#include "foreign_creature_array_window_controls.h"
#include "format_string.h"
#include "formation_type.h"
#include "function_handler.h"
#include "get_artifact_damage_modifier.h"
#include "hero.h"
#include "hero_class_properties.h"
#include "hero_keyword_replacer.h"
#include "interface_help.h"
#include "luck_icon_window.h"
#include "map_point.h"
#include "morale_icon_window.h"
#include "platform.h"
#include "player.h"
#include "replace_keywords.h"
#include "scrollbar.h"
#include "simple_dialog.h"
#include "skill_icon.h"
#include "skill_properties.h"
#include "spellbook_window.h"
#include "standard_fonts.h"
#include "text_window.h"
#include "toggle_button.h"
#include "town.h"
#include "town_type.h"

t_bitmap_group_cache g_army_layout( "dialog.army.layout" );

t_button_cache					g_dismiss_button( "button.dismiss" );
extern t_button_cache			g_divide_button;
extern t_button_cache			g_divide_single_button;
extern t_button_cache			g_ok_button;
extern t_button_cache const		k_move_garrison_down_button;
extern t_button_cache const		k_move_up_button;
static t_button_cache const		k_move_down_button( "button.move_down" );
static t_button_cache const		k_move_tombstone_down_button( "button.move_tombstone_down" );
static t_button_cache const		k_move_tombstone_up_button( "button.move_tombstone_up" );
extern t_external_string const	k_text_hero_level;
extern t_external_string const  k_text_not_applicable( "not_applicable.dialog" );
t_bitmap_group_cache            g_hide_potion_button( "button.hide_potion" );

extern t_bitmap_group_cache g_interface_800;
extern t_bitmap_group_cache g_interface_1024;
extern t_bitmap_group_cache g_interface_1280;

// -------------------------------------------------------------------
// Unnamed namespace
// -------------------------------------------------------------------
namespace
{
	// hotkey resource strings
	t_external_string const k_hot_key_cast_spell( "cast_spell_army.hot_keys" );				// C
	t_external_string const k_hot_key_dismiss_creature( "dismiss_creatures.hot_keys" );		// D
	t_external_string const k_hot_key_split_creatures( "split_creatures_army.hot_keys" );	// S
	t_external_string const k_hot_key_loose_formation( "loose_formation.hot_keys" );		// L
	t_external_string const k_hot_key_tight_formation( "tight_formation.hot_keys" );		// T
	t_external_string const k_hot_key_square_formation( "square_formation.hot_keys" );		// Q
	t_external_string const k_hot_key_biography( "biography.hot_keys" );					// B

    int k_max_army_windows = 5;

    struct t_sorted_artifacts_index
    {
        t_artifact  artifact;
        int         index;
    };
}

t_button* create_dialog_button( std::string const& name, t_bitmap_group_ptr bitmap_ptr, t_window *parent_window, t_screen_point offset )
{
	// create formation buttons
	t_button*		      button;
	t_window*             window;
	t_bitmap_layer const* layer;
	t_screen_rect         rect;

	layer = bitmap_ptr->find( name + "_released" );
	rect = layer->get_rect();
	button = new t_button( rect.top_left() + offset, parent_window );
	window = new t_bitmap_layer_window( layer, -rect.top_left(), button );
	button->set_released_image( window );
	layer = bitmap_ptr->find( name + "_pressed" );
	if (layer != 0 )
	{
	    window = new t_bitmap_layer_window( layer, -rect.top_left(), button );
    	button->set_pressed_image( window );
    }
    else
    {
	    layer = bitmap_ptr->find( name + "_depressed" );
    	if (layer != 0 )
    	{
    	    window = new t_bitmap_layer_window( layer, -rect.top_left(), button );
        	button->set_pressed_image( window );
        }
    }

   	layer = bitmap_ptr->find( name + "_disabled" );
	if (layer != 0 )
	{
		window = new t_bitmap_layer_window( layer, -rect.top_left(), button );
		button->set_disabled_image( window );
	}
	layer = bitmap_ptr->find( name + "_highlighted" );
	if (layer != 0 )
	{
		window = new t_bitmap_layer_window( layer, -rect.top_left(), button );
		button->set_highlighted_image( window );
	}
	button->update_size();

	return button;
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
t_army_dialog_base::t_army_dialog_base( t_creature_array* creatures, t_army* adjacent_army, 
							  t_adventure_frame* adventure_frame, int selected_slot )
			 : t_bitmap_layer_window( 0, t_screen_point(0,0), adventure_frame )
{
	assert( adventure_frame != 0 );

	m_layout = g_army_layout.get();
	m_adventure_frame = adventure_frame;
    m_creatures = creatures;
	m_adjacent_army = adjacent_army;
	m_selected_creature_array = 0;
	m_selected_window = 0;
	m_backpack_start = 0;
	m_selected_slot = -1;
	m_keyboard_on_adjacent_army = false;
	m_backpack_hide_potions = false;

	// find dimensions & center
	t_screen_point        origin(0,0);
	t_screen_point        creature_origin;
	t_bitmap_layer const* layer = m_layout->find( "background" );
	t_screen_rect		  rect = layer->get_rect();
	t_screen_rect         parent_rect = adventure_frame->get_client_rect();

	rect += t_screen_point( parent_rect.width() / 2, parent_rect.height() / 2);
	rect -= rect.size() / 2;
	set_bitmap( layer );
	move( rect );
	set_drop_shadow();

	// create backgrounds
	layer = m_layout->find( "hero_background" );
	m_hero_background = new t_bitmap_layer_window( layer, origin, this );
	m_hero_windows.push_back( m_hero_background.get() );
	layer = m_layout->find( "hero_inventory" );
	m_hero_inventory = new t_bitmap_layer_window( layer, origin, this );
	m_hero_windows.push_back( m_hero_inventory.get() );
	layer = m_layout->find( "creature_background" );
	creature_origin = layer->get_rect().top_left();
	m_creature_background = new t_bitmap_layer_window( layer, origin, this );
	m_creature_windows.push_back( m_creature_background.get() );
	rect = m_layout->find( "creature_box" )->get_rect() - creature_origin;
	m_creature_animation = new t_combat_model_window( rect, m_creature_background );

	t_artifact_slot slot;
	for (slot = t_artifact_slot(0); slot < k_artifact_slot_hero_count; enum_incr(slot))
	{
		m_artifact_sources[slot] = NULL;
    }
}

// ---------------------------------------------------------------------------
// Make sure any drag we started ends before we go away
// ---------------------------------------------------------------------------
void t_army_dialog_base::on_close()
{
	// Note this must be done no later than this, but this may already
	// be too late because of dependencies on other peer windows or
	// resources managed by the parent.  In that case, call cancel_drag 
	// somewhere earlier in the parent's shutdown chain
	cancel_drag();
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// Determine transfer costs of armies
// -----------------------------------------------------------------------
void t_army_dialog_base::get_transfer_costs( int & cost_in, int & cost_out ) const
{
	cost_in = 0;
	cost_out = 0;
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
void t_army_dialog_base::create_creature_windows()
{
	t_bitmap_layer const* layer;
	t_screen_point  origin(0,0);
	t_screen_rect   split_rect;
	t_screen_rect rect = m_layout->find( "creature_circles" )->get_rect();
	t_creature_array_window::t_layout first_line;
	t_help_block const& help = get_help_block( "army_screen" );

	if (m_adjacent_army == 0)
	{
	    layer = m_layout->find( "single_ring_background" );
	    m_ring_background = new t_bitmap_layer_window( layer, origin, this );
		first_line = t_creature_array_window::k_single_row;
	
	    split_rect = m_layout->find( "single_split_button" )->get_rect();
	    m_split_button = new t_toggle_button( g_divide_single_button.get(), split_rect.top_left(), this );
    	set_help( m_split_button, help, "split" );
    	m_split_button->set_click_handler( 
    		bound_handler( *this, &t_army_dialog_base::divide_clicked ) );
	}
	else
	{
	    layer = m_layout->find( "double_ring_background" );
	    m_ring_background = new t_bitmap_layer_window( layer, origin, this );
		first_line = t_creature_array_window::k_top_row;
        
        split_rect = m_layout->find( "split_button" )->get_rect();
	    m_split_button = new t_toggle_button( g_divide_button.get(), split_rect.top_left(), this );
    	set_help( m_split_button, help, "split" );
    	m_split_button->set_click_handler( 
    		bound_handler( *this, &t_army_dialog_base::divide_clicked ) );
	}
  m_ring_background->move_to_back();

	// determine transfer cost between armies
	int cost_in = 0;
	int cost_out = 0;

	get_transfer_costs( cost_in, cost_out );

	m_army_window = new t_creature_array_window( rect.top_left(), m_creatures, // m_army->get_creatures(), 
											     first_line, cost_in, this );
	m_army_window->set_select_handler( bound_handler( *this, &t_army_dialog_base::select ) );
	m_adjacent_army_window = 0;

	t_screen_rect  top_rect = m_army_window->get_screen_rect();
	t_screen_point bottom;

	top_rect = to_client( top_rect );
	bottom.x = top_rect.left;
	bottom.y = top_rect.top + m_army_window->get_frame_height();

	if ( m_adjacent_army == NULL )
	{
		m_move_up_button->set_visible( false );
		m_move_tombstone_up_button->set_visible( false );
		m_move_down_button->set_visible( false );
		m_move_tombstone_down_button->set_visible( false );
		m_move_garrison_down_button->set_visible( false );
		rect += t_screen_point(0, (rect.bottom - bottom.y) / 2 );
		m_army_window->move( rect );
		return;
	}

	// Select correct move up / move down button
	m_move_up_button->set_visible( !m_adjacent_army->is_graveyard() );
	m_move_tombstone_up_button->set_visible( m_adjacent_army->is_graveyard() );
	
	if ( m_creatures->is_graveyard() )
	{
		m_move_tombstone_down_button->set_visible( true );
		m_move_garrison_down_button->set_visible( false );
		m_move_down_button->set_visible( false );
	}
	else
	{
		m_move_tombstone_down_button->set_visible( false );
		if ( m_creatures->get_army() )
		{
			m_move_down_button->set_visible( true );
			m_move_garrison_down_button->set_visible( false );
		}
		else
		{
			m_move_down_button->set_visible( false );
			m_move_garrison_down_button->set_visible( true );
		}
	}


	m_adjacent_army_window 
		= new t_creature_array_window( bottom, &m_adjacent_army->get_creatures(), 
		                               t_creature_array_window::k_bottom_row, 
									   cost_out, this );
	m_adjacent_army_window->set_select_handler( bound_handler( *this, &t_army_dialog_base::select ) );
	m_adjacent_army_window->move_before( m_army_window );
	m_adjacent_army_window->link_selection( m_army_window );

#if (0)
	if ( m_army->is_graveyard() || m_adjacent_army->is_graveyard() )
	{
		m_adjacent_army_window->set_drag_validate_handler( bound_handler( *this, &t_army_dialog_base::validate_graveyard_drag_drop ) );
		m_army_window->set_drag_validate_handler( bound_handler( *this, &t_army_dialog_base::validate_graveyard_drag_drop ) );
	}
#endif
}


// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
t_button* t_army_dialog_base::create_button( std::string const& name, t_bitmap_group* layout_ptr )
{
	// create formation buttons
	t_button*		      button;
	t_window*             window;
	t_bitmap_layer const* layer;
	t_screen_rect         rect;
    t_bitmap_group*       layout;

    if (layout_ptr)
    {
        layout = layout_ptr;
    }
    else
    {
        layout = m_layout;
    }
    
	layer = layout->find( name + "_released" );
	rect = layer->get_rect();
	button = new t_button( rect.top_left(), this );
	window = new t_bitmap_layer_window( layer, -rect.top_left(), button );
	button->set_released_image( window );
	layer = layout->find( name + "_pressed" );
	window = new t_bitmap_layer_window( layer, -rect.top_left(), button );
	button->set_pressed_image( window );
	layer = layout->find( name + "_disabled" );
	if (layer != 0 )
	{
		window = new t_bitmap_layer_window( layer, -rect.top_left(), button );
		button->set_disabled_image( window );
	}
	layer = layout->find( name + "_highlighted" );
	if (layer != 0 )
	{
		window = new t_bitmap_layer_window( layer, -rect.top_left(), button );
		button->set_highlighted_image( window );
	}
	return button;
}


// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
t_toggle_button* t_army_dialog_base::create_toggle_button( std::string const& name )
{
	// create formation buttons
	t_toggle_button*      toggle_button;
	t_window*             window;
	t_bitmap_layer const* layer;
	t_screen_rect         rect;

	layer = m_layout->find( name + "_released" );
	rect = layer->get_rect();
	toggle_button = new t_toggle_button( rect.top_left(), this, true );
	window = new t_bitmap_layer_window( layer, -rect.top_left(), toggle_button );
	toggle_button->set_released_image( window );
	layer = m_layout->find( name + "_pressed" );
	window = new t_bitmap_layer_window( layer, -rect.top_left(), toggle_button );
	toggle_button->set_pressed_image( window );
	layer = m_layout->find( name + "_disabled" );
	if (layer != 0 )
	{
		window = new t_bitmap_layer_window( layer, -rect.top_left(), toggle_button );
		toggle_button->set_disabled_image( window );
	}
	return toggle_button;
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// create icons
// -----------------------------------------------------------------------
void t_army_dialog_base::create_icons()
{
	t_bitmap_layer const* layer;
	t_window*             window;
	t_screen_point        point(0,0);
	t_help_block const&   shared_help = get_help_block( "shared" );
	t_help_block const&   help = get_help_block( "army_screen" );

	layer = m_layout->find( "spell_points" );
	m_spell_points_icon = new t_bitmap_layer_window( layer, point, this );
	set_help( m_spell_points_icon, shared_help, "spell_points" );
	//m_hero_windows.push_back( m_spell_points_icon );

	layer = m_layout->find( "damage" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "damage" );

	layer = m_layout->find( "hit points" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "hit_points" );

	layer = m_layout->find( "move" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "adventure_movement" );

	layer = m_layout->find( "speed" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "speed" );

	layer = m_layout->find( "experience" );
	m_experience_icon = new t_bitmap_layer_window( layer, point, this );
	set_help( m_experience_icon, shared_help, "experience" );
	//m_hero_windows.push_back( m_experience_icon );

	layer = m_layout->find( "shots" );
	m_ammo_icon = new t_bitmap_layer_window( layer, point, this );
	set_help( m_ammo_icon, shared_help, "shots" );
	//layer = m_layout->find( "shots" );
	//m_shots_icon = new t_bitmap_layer_window( layer, point, this );
	//set_help( m_shots_icon, help, "shots" );

    // Added:
	layer = m_layout->find( "melee_attack" );
	m_melee_attack_icon = new t_bitmap_layer_window( layer, point, this );
	set_help( m_melee_attack_icon, help, "melee_attack" );

	layer = m_layout->find( "melee_defense" );
	m_melee_defense_icon = new t_bitmap_layer_window( layer, point, this );
	set_help( m_melee_defense_icon, help, "melee_defense" );

	layer = m_layout->find( "ranged_attack" );
	m_ranged_attack_icon = new t_bitmap_layer_window( layer, point, this );
	set_help( m_ranged_attack_icon, help, "ranged_attack" );

	layer = m_layout->find( "ranged_defense" );
	m_ranged_defense_icon = new t_bitmap_layer_window( layer, point, this );
	set_help( m_ranged_defense_icon, help, "ranged_defense" );

	t_screen_rect rect;

	rect = m_layout->find( "morale" )->get_rect();
	m_creature_morale_icon = new t_morale_icon_window( rect, this );
   	//set_help( m_creature_morale_icon, shared_help, "morale" );

	rect = m_layout->find( "luck" )->get_rect();
	m_creature_luck_icon = new t_luck_icon_window( rect, this );
   	//set_help( m_creature_luck_icon, shared_help, "luck" );

	// create portrait
	rect = m_layout->find( "creature_portrait" )->get_rect();
    m_portrait_button = new t_button( rect.top_left(), this );

	m_portrait = new t_bitmap_layer_cache_window( t_bitmap_layer_ptr(), 
		                                          t_screen_point(0, 0), m_portrait_button, false );
	layer = m_layout->find( "portrait_border" );
	window = new t_bitmap_layer_window( layer, point, this );

    m_portrait_button->set_released_image( m_portrait );
    m_portrait_button->enable( true );
    m_portrait_button->update_size();

	create_artifact_icons();
	create_skill_icons();

    // Scrollbar.
	rect = m_layout->find( "scrollbar" )->get_rect();
	m_backpack_scrollbar = new t_scrollbar( rect.top_left(), rect.height(), this, 0, 0, true );
	m_backpack_scrollbar->set_position( 0 );
	m_backpack_scrollbar->set_limits( 0, 0 );
    m_backpack_scrollbar->set_handler( bound_handler( *this, &t_army_dialog_base::scrollbar_move) );
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// create all buttons
// -----------------------------------------------------------------------
void t_army_dialog_base::create_buttons()
{
	// create split button
	t_screen_rect rect;
	t_button*     button;
	t_help_block const& help = get_help_block( "army_screen" );

	// create OK button
	rect = m_layout->find( "ok_button" )->get_rect();
	button = new t_button( g_ok_button.get(), rect.top_left(), this );
	set_help( button, help, "ok" );
	button->set_click_handler( bound_handler(*this, &t_army_dialog_base::close_click ) );

	// create formation buttons
	t_toggle_button*	toggle_button;
	t_button_handler	handler;
	 
	toggle_button = create_toggle_button( "loose" );
	handler = bound_handler( *this, &t_army_dialog_base::formation_loose );
	toggle_button->set_click_handler( handler );
	m_formation_group.add( toggle_button );
	set_help( toggle_button, help, "spread" );
 
	toggle_button = create_toggle_button( "tight" );
	handler = bound_handler( *this, &t_army_dialog_base::formation_tight );
	toggle_button->set_click_handler( handler );
	m_formation_group.add( toggle_button );
	set_help( toggle_button, help, "tight" );	

	toggle_button = create_toggle_button( "square" );
	handler = bound_handler( *this, &t_army_dialog_base::formation_square );
	toggle_button->set_click_handler( handler );
	m_formation_group.add( toggle_button );
	set_help( toggle_button, help, "square" );

	// create dismiss button
	rect = m_layout->find( "dismiss" )->get_rect();
	m_dismiss_button = new t_button( g_dismiss_button.get(), rect.top_left(), this );
	set_help( m_dismiss_button, help, "dismiss" );
	m_dismiss_button->set_click_handler( bound_handler( *this, &t_army_dialog_base::dismiss_creature ));
	m_creature_windows.push_back( m_dismiss_button.get() );

	rect = m_layout->find( "hide_potions_button" )->get_rect();
    m_hide_potions_button = create_dialog_button( "hide_potions", g_hide_potion_button.get(), this, rect.top_left() );
    m_hide_potions_button->set_visible( m_backpack_hide_potions );
	m_hide_potions_button->set_click_handler( bound_handler( *this, &t_army_dialog_base::hide_potions));
	set_help( m_hide_potions_button, help, "undo_hide_potions" );

    m_show_potions_button = create_dialog_button( "show_potions", g_hide_potion_button.get(), this, rect.top_left() );
    m_show_potions_button->set_visible( !m_backpack_hide_potions );
	m_show_potions_button->set_click_handler( bound_handler( *this, &t_army_dialog_base::hide_potions));
	set_help( m_show_potions_button, help, "hide_potions" );
	
    // create spellbook button
	m_spellbook_button = create_button( "spellbook" );
	set_help( m_spellbook_button, help, "spellbook" );
	m_spellbook_button->set_click_handler( bound_handler( *this, &t_army_dialog_base::spellbook_click ));
	m_hero_windows.push_back( m_spellbook_button.get() );
	
	// create biography button - double click on big hero portrait for bio.
#if (0)
	m_biography_button = create_button( "bio" );
	m_biography_button->set_click_handler(
		bound_handler( *this, &t_army_dialog_base::biography_click ) );
	set_help( m_biography_button, help, "biography" );
	m_hero_windows.push_back( m_biography_button.get() );
#endif

	// create backpack buttons
#if (0)
	m_backpack_up = create_button( "backpack_up" );
	m_backpack_up->set_click_handler( bound_handler( *this, &t_army_dialog_base::backpack_up_click ) );
	set_help( m_backpack_up, help, "backpack_scroll" );

	m_backpack_down = create_button( "backpack_down" );
	m_backpack_down->set_click_handler( bound_handler( *this, &t_army_dialog_base::backpack_down_click ) );
	set_help( m_backpack_down, help, "backpack_scroll" );
#endif

	// create move up / move down
	t_screen_point          point(0,0);

    point = m_layout->find("move_army_up")->get_rect().top_left();
	m_move_up_button = new t_button( k_move_up_button.get(), point, this );
	m_move_up_button->set_click_handler( bound_handler( *this, &t_army_dialog_base::move_up_clicked ) );
	set_help( m_move_up_button, help, "move_army_up" );	

	m_move_tombstone_up_button = new t_button( k_move_tombstone_up_button.get(), point, this );
	m_move_tombstone_up_button->set_click_handler( bound_handler( *this, &t_army_dialog_base::move_up_clicked ) );
	set_help( m_move_tombstone_up_button, help, "move_army_up" );	

    point = m_layout->find("move_army_down")->get_rect().top_left();
	m_move_down_button = new t_button( k_move_down_button.get(), point, this );
	m_move_down_button->set_click_handler( bound_handler( *this, &t_army_dialog_base::move_down_clicked ) );
	set_help( m_move_down_button, help, "move_army_down" );	

	m_move_garrison_down_button = new t_button( k_move_garrison_down_button.get(), point, this );
	m_move_garrison_down_button->set_click_handler( bound_handler( *this, &t_army_dialog_base::move_down_clicked ) );
	set_help( m_move_garrison_down_button, help, "move_army_down" );	

	m_move_tombstone_down_button = new t_button( k_move_tombstone_down_button.get(), point, this );
	m_move_tombstone_down_button->set_click_handler( bound_handler( *this, &t_army_dialog_base::move_down_clicked ) );
	set_help( m_move_tombstone_down_button, help, "move_army_down" );
}

void t_army_dialog_base::formation_loose( t_button* button )
{
	if (button->is_pressed())
		m_selected_creature_array->set_formation( k_formation_loose );
}

void t_army_dialog_base::formation_square( t_button* button )
{
	if (button->is_pressed())
		m_selected_creature_array->set_formation( k_formation_square );
}

void t_army_dialog_base::formation_tight( t_button* button )
{
	if (button->is_pressed())
		m_selected_creature_array->set_formation( k_formation_tight );
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
void t_army_dialog_base::divide_clicked( t_button* button )
{
	if (button->is_pressed())
	{
		m_army_window->set_divide_mode( bound_handler( *this, 
		                                &t_army_dialog_base::cancel_divide_mode ));
		if (m_adjacent_army_window != 0)
			m_adjacent_army_window->set_divide_mode( bound_handler( *this,
													 &t_army_dialog_base::cancel_divide_mode ));
	}
	else
	{
		cancel_divide_mode();
	}

}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
void t_army_dialog_base::move_down_clicked( t_button* )
{
	if( m_adjacent_army != 0 )
	{
		assert( !is_restricted( false ) );

		t_adventure_map & map = *m_adventure_frame->get_map();
		int player_number = map.get_player_number();

		bool target_is_allied = ( m_adjacent_army->get_owner_number() != player_number );
		bool target_is_graveyard = ( m_adjacent_army->is_graveyard() );

		// Don't move heroes into allied armies. Don't move anything except 
		// dead heroes into graveyards
		m_adjacent_army_window->merge( m_army_window, !target_is_graveyard, !target_is_allied && !target_is_graveyard, !target_is_allied );
	}
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
void t_army_dialog_base::move_up_clicked( t_button* )
{
	if( m_adjacent_army != 0 )
	{
		assert( !is_restricted( true ) );

		t_adventure_map & map = *m_adventure_frame->get_map();
		int player_number = map.get_player_number();

		bool target_is_allied = ( m_creatures->get_owner_number() != player_number );
		bool target_is_graveyard = ( m_creatures->is_graveyard() );

		// Don't move heroes into allied armies. Don't move anything except 
		// dead heroes into graveyards
		m_army_window->merge( m_adjacent_army_window, !target_is_graveyard, !target_is_allied && !target_is_graveyard, !target_is_allied );
	}
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
void t_army_dialog_base::cancel_divide_mode()
{
	m_split_button->set_pressed( false );
	m_army_window->cancel_divide_mode();
	if (m_adjacent_army_window != 0)
		m_adjacent_army_window->cancel_divide_mode();
}

// -----------------------------------------------------------------------
// Create backpack slots for artifacts.
// -----------------------------------------------------------------------
void t_army_dialog_base::create_artifact_icons()
{
	t_screen_rect   rect;
	int i;

	for (i = 0; i < k_backpack_size; i++)
	{
		std::string name = format_string( "backpack %i slot", i + 1 );

		rect = m_layout->find( name )->get_rect();
		m_backpack[i] = new t_drag_artifact_source( rect, k_artifact_slot_none, this,
			                                        this );
	}
}

/*
** Create slots for hero artifacts.
*/
void t_army_dialog_base::create_hero_artifact_icons()
{
	t_artifact_slot slot;
	t_screen_rect   rect;
	t_screen_point  origin;
	t_screen_point  highlight_offset;

	t_bitmap_layer const* highlight_image = m_hero_inventory_bitmap->find( "artifact_highlight" );

	highlight_offset = highlight_image->get_rect().top_left() 
					 - m_hero_inventory_bitmap->find( "head" )->get_rect().top_left();
	origin = to_client( m_hero_inventory->get_screen_rect().top_left() );

	for (slot = t_artifact_slot(0); slot < k_artifact_slot_hero_count; enum_incr(slot))
	{
	    t_screen_rect   artifact_rect;
		std::string name = get_keyword( slot );

		//rect = m_hero_inventory_bitmap->find( name + " slot" )->get_rect() - origin;
		rect = m_hero_inventory_bitmap->find( name )->get_rect() + origin;
		if (m_highlight[slot] != 0)
			m_highlight[slot]->close();
		m_highlight[slot] = new t_bitmap_layer_window( highlight_image, 
			                                           rect.top_left() + highlight_offset,
			                                           this, false ); //m_hero_inventory, false );
		m_highlight[slot]->set_visible( false );
		
        artifact_rect = m_hero_inventory_bitmap->find( name )->get_rect();
		if (m_artifact_sources[slot])
		    m_artifact_sources[slot]->close();
		
        m_artifact_sources[slot]
			= new t_drag_artifact_source( artifact_rect, slot, this, m_hero_inventory );
	}
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
void t_army_dialog_base::create_skill_icons()
{
	t_screen_rect rect;
	int			  row;
	int			  column;
	int			  columns[k_skill_columns];
	int			  rows[k_skill_rows];
	std::string   name;

	rect = m_layout->find( "skill_1" )->get_rect();
	columns[0] = rect.left;
	rows[0] = rect.top;
	rect -= rect.top_left();

	for (column = 1; column < k_skill_columns; column++)
	{
		name = format_string( "skill_%i", column + 1 );
		columns[column] = m_layout->find( name )->get_rect().left;
	}

	for (row = 1; row < k_skill_rows; row++)
	{
		name = format_string( "skill_row_%i", row + 1 );
		rows[row] = m_layout->find( name )->get_rect().top;
	}

	for (row = 0; row < k_skill_rows; row++)
	{
		for (column = 0; column < k_skill_columns; column++)
		{
			t_screen_point point( columns[column], rows[row] );

			m_skills[row][column] = new t_skill_icon( t_skill(), rect + point, this );	
		}
	}

	// create borders
	t_bitmap_layer const*	layer;
	t_window*				window;

	layer = m_layout->find( "abilities_frame" );
	window = new t_bitmap_layer_window( layer, t_screen_point(0,0), this );
	m_creature_windows.push_back( window );
	layer = m_layout->find( "skill_frame" );
	window = new t_bitmap_layer_window( layer, t_screen_point(0,0), this );
	m_hero_windows.push_back( window );
}

// -----------------------------------------------------------------------
// Create text.
// -----------------------------------------------------------------------
void t_army_dialog_base::create_text_boxes()
{
	t_screen_rect rect;
	t_pixel_24    color(0,0,0);
	t_pixel_24    drop_shadow_color(200,200,200);

	rect = m_layout->find( "name_text" )->get_rect();
	m_name = new t_text_window( get_font( 16 ), rect, this, "", color);
	m_name->set_drop_shadow( true, drop_shadow_color );

	rect = m_layout->find( "class_text" )->get_rect();
	m_class = new t_text_window( get_font( 16 ), rect, this, "", color );
	m_class->set_drop_shadow( true, drop_shadow_color);
	m_hero_windows.push_back( m_class.get() );

	rect = m_layout->find( "spell_points_text" )->get_rect();
	m_spell_points = new t_text_window( get_font( 14 ), rect, this, "", color );
	m_spell_points->set_drop_shadow( true, drop_shadow_color );
	m_spell_points->set_center_horizontal();
	m_spell_points->set_center_vertical();
	m_hero_windows.push_back( m_spell_points.get() );

	rect = m_layout->find( "damage_text" )->get_rect();
	m_damage = new t_text_window( get_font( 14 ), rect, this, "", color );
	m_damage->set_center_horizontal();
	m_damage->set_center_vertical();
	m_damage->set_drop_shadow( true, drop_shadow_color );

	rect = m_layout->find( "hit_points_text" )->get_rect();
	m_health = new t_text_window( get_font( 14 ), rect, this, "", color );
	m_health->set_center_horizontal();
	m_health->set_center_vertical();
	m_health->set_drop_shadow( true, drop_shadow_color );

	rect = m_layout->find( "move_text" )->get_rect();
	m_movement = new t_text_window( get_font( 14 ), rect, this, "", color );
	m_movement->set_center_horizontal();
	m_movement->set_center_vertical();
	m_movement->set_drop_shadow( true, drop_shadow_color );

	rect = m_layout->find( "speed_text" )->get_rect();
	m_speed = new t_text_window( get_font( 14 ), rect, this, "", color );
	m_speed->set_center_horizontal();
	m_speed->set_center_vertical();
	m_speed->set_drop_shadow( true, drop_shadow_color );

	rect = m_layout->find( "shots_text" )->get_rect();
	m_shots = new t_text_window( get_font( 14 ), rect, this, "", color );
	m_shots->set_center_horizontal();
	m_shots->set_center_vertical();
	m_shots->set_drop_shadow( true, drop_shadow_color );

	rect = m_layout->find( "experience_text" )->get_rect();
	m_experience = new t_text_window( get_font( rect.height() ), rect, this, "", color );
	m_experience->set_center_horizontal();
	m_experience->set_center_vertical();
	m_experience->set_drop_shadow( true, drop_shadow_color );
	m_hero_windows.push_back( m_experience.get() );

    // Added:
	rect = m_layout->find( "melee_attack_text" )->get_rect();
	m_melee = new t_text_window( get_font( 14 ), rect, this, "", color );
	m_melee->set_center_horizontal();
	m_melee->set_center_vertical();
	m_melee->set_drop_shadow( true, drop_shadow_color );

	rect = m_layout->find( "melee_defense_text" )->get_rect();
	m_melee_defense = new t_text_window( get_font( 14 ), rect, this, "", color );
	m_melee_defense->set_center_horizontal();
	m_melee_defense->set_center_vertical();
	m_melee_defense->set_drop_shadow( true, drop_shadow_color );

	rect = m_layout->find( "ranged_attack_text" )->get_rect();
	m_ranged_attack = new t_text_window( get_font( 14 ), rect, this, "", color );
	m_ranged_attack->set_center_horizontal();
	m_ranged_attack->set_center_vertical();
	m_ranged_attack->set_drop_shadow( true, drop_shadow_color );

	rect = m_layout->find( "ranged_defense_text" )->get_rect();
	m_ranged_defense = new t_text_window( get_font( 14 ), rect, this, "", color );
	m_ranged_defense->set_center_horizontal();
	m_ranged_defense->set_center_vertical();
	m_ranged_defense->set_drop_shadow( true, drop_shadow_color );

	rect = m_layout->find( "creature_text" )->get_rect();
	m_creature_text = new t_text_window( get_font( 20 ), rect, this, "", color );
	m_creature_text->set_center_vertical();
	m_creature_text->set_drop_shadow( true, drop_shadow_color );
	m_creature_windows.push_back( m_creature_text.get() );

	rect = m_layout->find( "morale_text")->get_rect();
	m_creature_morale_text = new t_text_window( get_font( rect.height() ), rect, this, "", color );
	m_creature_morale_text->set_center_horizontal();
	m_creature_morale_text->set_center_vertical();
	m_creature_morale_text->set_drop_shadow( true, drop_shadow_color );

	rect = m_layout->find( "luck_text")->get_rect();
	m_creature_luck_text = new t_text_window( get_font( rect.height() ), rect, this, "", color );
	m_creature_luck_text->set_center_horizontal();
	m_creature_luck_text->set_center_vertical();
	m_creature_luck_text->set_drop_shadow( true, drop_shadow_color );
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
void t_army_dialog_base::close_click( t_button* button )
{
	close_creature_windows();
	close();
}

// -----------------------------------------------------------------------
// caches for various backgrounds
// -----------------------------------------------------------------------
static t_bitmap_group_ptr get_inventory_bitmap( t_hero const* hero )
{
	static bool initialized = false;

	static t_bitmap_group_cache caches[k_town_type_count][2][2];
	
	if (!initialized)
	{
		t_town_type       town;
		int               sex;
		int               type;
		std::string       name;

		for (town = t_town_type(0); town < k_town_type_count; enum_incr(town))
		{
			for (sex = 0; sex < 2; sex++)
			{
				for (type = 0; type < 2; type++)
				{
					name = "dialog.army." + get_type_keyword( town, sex != 0, type != 0 );
					caches[town][sex][type] = t_bitmap_group_cache( name );
				}
			}
		}
		initialized = true;
	}
	return caches[hero->get_alignment()][hero->is_male()]
		         [hero->uses_spellcaster_model()].get();
}

// -----------------------------------------------------------------------
// Update text. (creature stats)
// select a new creature to display
// -----------------------------------------------------------------------
extern t_external_string const k_text_infinity_abbreviation( "infinity_abbreviation.misc" );

void t_army_dialog_base::update_statistics()
{
	t_creature_array const& army = *m_selected_creature_array;
	t_creature_stack const& slot = army[m_selected_slot];

	if (slot.get_number() == 0)
		return;

	t_hero const* hero = slot.get_hero();
	int			  low_damage;
	int			  high_damage;
	bool		  ranged = slot.has_ability( k_ability_ranged );

	low_damage = slot.get_damage_low();
	high_damage = slot.get_damage_high();
	if (hero != 0)
	{
		std::string      text;
		std::vector<int> damage;
		int				 artifact_damage;

		m_spell_points->set_text( format_string( "%i\n(%i)",
			                      hero->get_spell_points(), 
								  hero->get_maximum_spell_points() ) );
		artifact_damage = get_artifact_damage( *hero, ranged, damage );
		low_damage += artifact_damage;
		high_damage += artifact_damage;
	}
	else
	{
		t_creature const& creature = *slot.get_creature();

		if (slot.get_maximum_spell_points() > 0)
		{
			m_spell_points->set_text( format_string( "%i", slot.get_maximum_spell_points() ) );
			m_spell_points->set_visible( true );
			m_spell_points_icon->set_visible( true );
		}
	}

	if ( low_damage < 1 )
		low_damage = 1;

	if ( high_damage < 1 )
		high_damage = 1;

	m_damage->set_text( format_string( "%i-%i", low_damage, high_damage ));

	std::string text;

	if (slot.get_wounds() > 0)
    {
		text = format_string( "%i\n(%i)", slot.get_hit_points() - slot.get_wounds(),
		                      slot.get_hit_points() );
    }
	else
    {
		text = format_string( "%i", slot.get_hit_points() );
    }
	m_health->set_text( text );

	int maximum  = army.get_max_movement( slot );
	int movement = army.get_movement( slot );

	m_movement->set_text( format_string( "%i\n(%i)", movement / 100, maximum / 100 ) );
	m_speed->set_text( format_string( "%i", slot.get_speed( army ) ) );

	int			shots = slot.get_shots();
	int			morale = slot.get_morale( army );
	int			luck   = slot.get_luck( army );

	//m_shots->set_visible( shots > 0 );
	//m_ammo_icon->set_visible( shots > 0 );
	if (slot.has_ability( k_ability_unlimited_shots ))
		m_shots->set_text( k_text_infinity_abbreviation );
    //else if (shots > 0)
    else if (slot.has_ability( k_ability_ranged ))
		m_shots->set_text( format_string( "%i", shots ) );
	else
		m_shots->set_text( k_text_not_applicable );

	m_creature_morale_icon->set_creature( &slot, m_selected_creature_array );
	if (morale <= 0)
		text = format_string( "%i", morale );
	else if (morale > 0)
		text = format_string( "+%i", morale );
	m_creature_morale_text->set_text( text );

	m_creature_luck_icon->set_creature( &slot );
	if (luck <= 0)
		text = format_string( "%i", luck );
	else if (luck > 0)
		text = format_string( "+%i", luck );
	m_creature_luck_text->set_text( text );
    m_creature_luck_icon->set_value( luck );

    // Melee attack.
	//int damage_low  = slot.get_damage_low();
	//int damage_high = slot.get_damage_high();

	float melee_offense = slot.get_offense( false, army );
    //text = format_string( "%i-%i", damage_low, damage_high );
	//if (melee_offense != 1.0f)
	text = format_string( "%i", int( melee_offense * 10.0f + 0.5f ) );
    m_melee->set_text( text );

    // Melee defense.
	float melee_defense = slot.get_defense( false, army );
	text = format_string( "%i", int( melee_defense * 10.0f + 0.5f ) );
	m_melee_defense->set_text( text );

	if (slot.has_ability( k_ability_ranged ))
	{
        // Ranged attack.
    	float offense = slot.get_offense( true, army );
    	text = format_string( "%i", int( offense * 10.f + 0.5f ) );
        m_ranged_attack->set_text( text );
    }
    else
    {
        m_ranged_attack->set_text( k_text_not_applicable );
    }

    // Ranged defense.
    float ranged_defense = slot.get_defense( true, army );

    text = format_string( "%i", int( ranged_defense * 10.0f + 0.5f ));
    m_ranged_defense->set_text( text );
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// select a new creature to display
// -----------------------------------------------------------------------

void t_army_dialog_base::select( t_creature_array_window* window, int index )
{
	t_window_list::iterator	window_iter;
	t_creature_array*		army = window->get_army();


	if (army == 0 || index < 0)
	{
		for (window_iter = m_hero_windows.begin(); window_iter != m_hero_windows.end(); window_iter++)
		{
			(*window_iter)->set_visible( false );
		}
		for (window_iter = m_creature_windows.begin(); window_iter != m_creature_windows.end(); window_iter++)
		{
			(*window_iter)->set_visible( false );
		}
	  m_portrait_button->set_click_handler( NULL );
		set_skill_icons( NULL );
		m_formation_group.set_visible( false );
		return;
	}


	t_adventure_map & map = *m_adventure_frame->get_map();
	int player_number = map.get_player_number();

	t_creature_stack& slot = (*army)[index];

	if (slot.get_number() == 0)
		return;

	if (army != m_selected_creature_array || index != m_selected_slot)
		m_backpack_start = 0;

	m_selected_creature_array = army;
	m_selected_slot = index;
	m_selected_window = window;

	t_hero const* hero = slot.get_hero();

	for (window_iter = m_hero_windows.begin(); window_iter != m_hero_windows.end(); window_iter++)
	{
		(*window_iter)->set_visible( hero != 0 );
	}

	for (window_iter = m_creature_windows.begin(); window_iter != m_creature_windows.end(); window_iter++)
	{
		(*window_iter)->set_visible( hero == 0 );
	}

	if (hero != 0)
	{
		std::string text;

		text = replace_keywords( k_text_hero_level, "%level", format_string( "%i", hero->get_level() ),
			                     "%class_name", hero->get_class_name() );
		m_class->set_text( text );
		m_class->set_right_click_text( hero->get_class_description() );
		set_skill_icons( hero );
		m_name->set_text( slot.get_name() );
		m_experience->set_text( abbreviate_number( hero->get_experience(),
												   *m_experience->get_font(),
												   m_experience->get_width() ));

    std::string experience_icon_text;
    int exp_needed = hero->get_experience_needed_for_next_level();

    if (exp_needed)
    {
      experience_icon_text = replace_keywords( get_element_help("shared", "show_experience"),
        "%amount", format_string("%d", hero->get_experience_needed_for_next_level()) );
    }
    else
    {
      experience_icon_text = get_element_help("shared", "show_experience_full");
    }

    m_experience_icon->set_right_click_text( experience_icon_text );
    m_hero_inventory_bitmap = get_inventory_bitmap( hero );
    //m_hero_inventory->set_bitmap( m_hero_inventory_bitmap->front().get() );
    m_hero_inventory->set_bitmap( m_hero_inventory_bitmap->find( "background" ) );
    m_hero_inventory->update_size();

    // Set portrait click to be biography.
    m_portrait_button->set_click_handler( bound_handler( *this, &t_army_dialog_base::biography_click ) );
    m_portrait_button->set_right_click_text( hero->get_biography() );
    create_hero_artifact_icons();		
	}
	else
	{
		t_creature const& creature = *slot.get_creature();

		set_skill_icons( creature.get_creature_type() );
		m_name->set_text( format_string( "%i ", slot.get_number() ) + slot.get_name() );
		m_creature_text->set_text( creature.get_traits().help_text );
		m_creature_animation->set( creature.get_creature_type() );
        m_experience_icon->set_right_click_text( get_element_help("shared", "creature_experience") );
		if (slot.can_cast_spells())
			m_spellbook_button->set_visible( true );
	    m_portrait_button->set_click_handler( NULL );
		m_portrait_button->set_right_click_text( creature.get_traits().short_help_text );
	}

	if ( m_dismiss_button )
	{
		// Cannot dismiss other player's troops
		m_dismiss_button->enable( !is_restricted( m_selected_creature_array ) );
	}

	m_formation_group.set_visible( true );
	m_formation_group.select( m_selected_creature_array->get_formation() );

	m_formation_group.enable( !is_restricted( m_selected_creature_array ) );
	// Always highlight selected choice	
	m_formation_group.enable( m_selected_creature_array->get_formation(), true );

	m_portrait->set_bitmap( slot.get_portrait( 82 ) );
	m_portrait->set_help_balloon_text( slot.get_name() );
	set_artifact_icons( slot );
	update_statistics();
}

static t_external_string const k_nobility_string_no_town( "nobility_string_no_town.misc" );
static t_external_string const k_nobility_string_ruling( "nobility_string_ruling.misc" );
static t_external_string const k_nobility_string_overruled( "nobility_string_overruled.misc" );

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// setup skill icons for a hero
// -----------------------------------------------------------------------
void t_army_dialog_base::set_skill_icons( t_hero const* hero )
{
	int				row;
	int				column;
	t_skill_type	skill;
	t_skill_mastery level;
	t_skill_type	primary_skill;
	int				skill_row[k_skill_primary_count];
	int				columns[k_skill_rows];

	memset( columns, 0, sizeof(columns ));
	for (skill = t_skill_type(0); skill < k_skill_primary_count; enum_incr( skill ))
	{
		skill_row[skill] = -1;
	}

	if (hero)
	{
		int row_count = 0;
		for (skill = t_skill_type(0); skill < k_skill_count; enum_incr( skill ))
		{
			level = hero->get_skill( skill );
			if (level == k_mastery_none)
				continue;

			primary_skill = get_primary_skill( skill );
			if (skill_row[primary_skill] == -1)
			{
				if (row_count == k_skill_rows)
					continue;
				columns[row_count] = 0;
				skill_row[primary_skill] = row_count++;
			}
			row = skill_row[primary_skill];
			column = columns[row]++;
			if (column >= k_skill_columns)
				continue;
			m_skills[row][column]->set_skill( t_skill( skill, level ) );

			if ( skill == k_skill_nobility )
			{
				// Special popup text for nobility, includes town being governed, other data
				std::string popup_text;
				t_hero_keyword_replacer replacer;

				replacer.add_hero( hero );

				t_abstract_town_ptr town = hero->get_nobility_town();

				if ( town.get() == NULL )
				{
					popup_text = k_nobility_string_no_town;
				}
				else
				{
					replacer.add_keyword( "%town_name", town->get_name() );

					t_hero * ruler = town->get_best_nobility_hero();

					// tell the player we're ruling a town if we're the ruler,
					// or if no hero is (which means we're ineligible for some reason).
					if ( ruler == 0 )
						popup_text = k_nobility_string_no_town;
					else if ( ruler == hero)
					{
						popup_text = k_nobility_string_ruling;
					}
					else
					{
						replacer.add_keyword("%other_hero", ruler->get_name() );
						popup_text = k_nobility_string_overruled;
					}
				}

				popup_text = ::get_help_text( t_skill( skill, level ) ) + replacer( popup_text );

				m_skills[row][column]->set_custom_popup_text( popup_text );
			}
			else
			{
				m_skills[row][column]->clear_custom_popup_text();
			}

			m_skills[row][column]->set_visible( true );
		}
	}

	for (row = 0; row < k_skill_rows; row++)
	{
		for (column = columns[row]; column < k_skill_columns; column++)
		{
			m_skills[row][column]->set_visible( false );
			m_skills[row][column]->clear_custom_popup_text();
		}
	}
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// setup artifact icons
// -----------------------------------------------------------------------
void t_army_dialog_base::set_artifact_icons( t_creature_stack& stack )
{
	t_hero*			hero = stack.get_hero();
	t_artifact_slot slot;
	int             i;
	int             backpack_slot;
	int             slot_count = 0;
	t_stack_with_backpack& backpack = dynamic_cast<t_stack_with_backpack&>( stack );
	std::vector<t_sorted_artifacts_index> sorted_artifacts;

    // Sort potions last.
    if (m_backpack_hide_potions)
    {
	    for (i = 0; i < backpack.get_backpack_count(); i++)
        {
            if (!(backpack.get_backpack_slot(i).is_potion()))
            {
                t_sorted_artifacts_index artifact_index;
                artifact_index.artifact = backpack.get_backpack_slot(i);
                artifact_index.index = i;
                sorted_artifacts.push_back( artifact_index );
            }
        }
	    for (i = 0; i < backpack.get_backpack_count(); i++)
        {
            if (backpack.get_backpack_slot(i).is_potion())
            {
                t_sorted_artifacts_index artifact_index;
                artifact_index.artifact = backpack.get_backpack_slot(i);
                artifact_index.index = i;
                sorted_artifacts.push_back( artifact_index );
            }
        }
    }

	for (i = 0; i < k_backpack_size; i++)
	{
		backpack_slot = i + m_backpack_start;
		if (backpack_slot >= backpack.get_backpack_count())
			m_backpack[i]->set( t_artifact(), &stack, backpack_slot );
		else
        {
	        if (!m_backpack_hide_potions)
            {
    			m_backpack[i]->set( backpack.get_backpack_slot( backpack_slot ), &stack, 
			                               backpack_slot );
            }
            else if (!(backpack.get_backpack_slot( backpack_slot ).is_potion()))
            {
    			m_backpack[i]->set( sorted_artifacts[backpack_slot].artifact, &stack, 
			                               sorted_artifacts[backpack_slot].index );
                slot_count++;
            }
            //else
			//    m_backpack[i]->set( t_artifact(), &stack, backpack_slot );
        }
	}

    if (m_backpack_hide_potions)
    {
    	for (i = 0; i < k_backpack_size; i++)
    	{
    		backpack_slot = i + m_backpack_start;
    		if (backpack_slot >= backpack.get_backpack_count())
    			m_backpack[i]->set( t_artifact(), &stack, backpack_slot );
    		else
            {
                if (backpack.get_backpack_slot( backpack_slot ).is_potion())
                {
        			m_backpack[i]->set( sorted_artifacts[backpack_slot].artifact, &stack, 
			                               sorted_artifacts[backpack_slot].index );
                    slot_count++;
                }
            }
        }
	}

   	int total_items = backpack.get_backpack_count();

   	if ( total_items > k_backpack_size )
    {
   	    m_backpack_scrollbar->set_visible( true );
        // Up by 2s because we have a 2x5 grid.
    	m_backpack_scrollbar->set_limits( 0, ((total_items - k_backpack_size)+1)/2 );
    }
    else
    {
	    m_backpack_start = 0;
   	    m_backpack_scrollbar->set_visible( false );
    }
	//m_backpack_up->enable( m_backpack_start > 0 );
	//m_backpack_down->enable( m_backpack_start + k_backpack_size < backpack.get_backpack_count() );

	if (hero)
	{
   		for (slot = t_artifact_slot(0); slot < k_artifact_slot_hero_count; enum_incr( slot ))
   		{
   			m_artifact_sources[slot]->set( hero->get_artifact( slot ), hero );
   		}
	}

	highlight_artifact_slots();
}

// -----------------------------------------------------------------------
// Called by t_drag_artifact_source when drag is complete
// -----------------------------------------------------------------------
void t_army_dialog_base::artifact_accepted( t_drag_artifact_source * source, t_window * target )
{
	// Don't care
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// setup skill icons for a creature
// -----------------------------------------------------------------------
void t_army_dialog_base::set_skill_icons( t_creature_type creature )
{
	int row, column;
	int i;
	t_creature_traits const& traits = get_traits( creature );

	row = 0;
	column = 0;
	for (i = 0; i < traits.abilities.size(); i++)
	{
		m_skills[row][column]->set_ability( traits.abilities[i] );
		m_skills[row][column]->set_visible( true );
		m_skills[row][column]->clear_custom_popup_text();
		column++;
		if (column == k_skill_columns)
		{
			column = 0;
			row++;
		}
	}

	while ( row < k_skill_rows )
	{
		m_skills[row][column]->set_visible( false );
		m_skills[row][column]->clear_custom_popup_text();
		column++;
		if (column == k_skill_columns)
		{
			column = 0;
			row++;
		}
	}
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// close creature windows
// -----------------------------------------------------------------------
void t_army_dialog_base::close_creature_windows()
{
	m_army_window->close();
	m_ring_background->close();
	m_split_button->close();

	if (m_adjacent_army_window != 0)
		m_adjacent_army_window->close();
	m_army_window = 0;
	m_adjacent_army_window = 0;
	m_adjacent_army = 0;
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// show biography
// -----------------------------------------------------------------------
void t_army_dialog_base::biography_click( t_button* )
{
	assert( m_selected_creature_array != 0 );

	t_hero const* hero = (*m_selected_creature_array)[m_selected_slot].get_hero();

	if ( hero )
	{
		ok_dialog( hero->get_biography(), true );
	}
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// respond to a menu click
// -----------------------------------------------------------------------
bool t_army_dialog_base::menu_click( int id )
{
	if (m_selected_creature_array == 0)
		return false;
	if (m_selected_slot < 0)
		return false;

	t_artifact artifact;

	if (create_artifact_cheat( id, artifact ))
	{
		t_creature_stack& stack = (*m_selected_creature_array)[m_selected_slot];

		stack.add( artifact );
		set_artifact_icons( stack );
		return true;
	}
	return false;
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// accept an artifact drag
// -----------------------------------------------------------------------
void t_army_dialog_base::accept_artifact_drag( t_drag_artifact* drag, t_drag_artifact_source* dest )
{
	if (!can_accept( drag, dest ))
		return;

	t_artifact        artifact = drag->get_artifact();
	t_creature_stack* stack = dest->get_stack();
	t_artifact_slot   slot = dest->get_slot();

	if (slot != k_artifact_slot_none)
	{
		// place artifact in slot, and remove old one.
		t_hero*    hero = stack->get_hero();
		t_artifact old_artifact = hero->remove( slot );

		// if placing in the shield slot, check if the target has a 2-handed weapon
		if (old_artifact.get_icon() == k_artifact_none && slot == k_artifact_slot_shield )
		{
			t_artifact_type weapon = hero->get_artifact( k_artifact_slot_weapon ).get_icon();

			// if the target has a 2-handed weapon, remove it.
			if (weapon != k_artifact_none && get_slot( weapon ) == k_artifact_slot_2_handed )
				old_artifact = hero->remove( k_artifact_slot_weapon );
		}
		// if placing in the weapon slot, and the current weapon is 2 handed, check if
		// we must replace a shield
		if ( get_slot( artifact.get_icon() ) == k_artifact_slot_2_handed)
		{
			if ( old_artifact.get_icon() != k_artifact_none)
				drag->get_source()->restore( old_artifact );
			old_artifact = hero->remove( k_artifact_slot_shield );
		}

		if (hero->equip( artifact, slot ))
		{
			if (artifact.get_icon() == k_artifact_parchment)
			{
				std::string message;

				if (use_parchment( *hero, artifact, &message ))
					hero->remove( slot );
				show_popup_text( message, get_mouse_screen_position() );
			}
		}
		else
		{
			hero->add( artifact );
		}
		if (old_artifact.get_icon() != k_artifact_none)
			drag->get_source()->restore( old_artifact );
	}
	else
	{
		stack->add_to_backpack( artifact, dest->get_backpack_slot() );
	}
	update_statistics();
	drag->accepted( this );
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// accept an artifact drag
// -----------------------------------------------------------------------
void t_army_dialog_base::begin_artifact_drag( t_drag_artifact_source* source )
{
	t_artifact        artifact;
	t_artifact_slot   slot = source->get_slot();
	t_creature_stack* stack = source->get_stack();

    // Don't allow player to manipulate / steal ally's artifacts
	t_creature_array const * selected_army = get_selected_army();

	assert( selected_army );

	if ( selected_army )
	{
		if ( is_restricted( selected_army ) )
		{
			return;
		}
	}

	if (slot == k_artifact_slot_none)
		artifact = stack->remove_backpack( source->get_backpack_slot() );
	else
		artifact = stack->get_hero()->remove( slot );

	update_artifacts();

	t_drag_artifact* drag = new t_drag_artifact( artifact, source );

	begin_drag( drag );

	highlight_artifact_slots();
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// Turn on or off highlighting of artifact slots for current drag artifact
// -----------------------------------------------------------------------
void t_army_dialog_base::highlight_artifact_slots()
{
	t_drag_artifact * drag_artifact = dynamic_cast< t_drag_artifact * >( get_drag_object() );
	t_stack_with_backpack * stack = get_selected_stack();

	t_hero          * hero = NULL;

	if ( stack )
	{
		hero = stack->get_hero();
	}

	if ( drag_artifact && hero )
	{
		for (t_artifact_slot slot = t_artifact_slot(0); slot < k_artifact_slot_hero_count; enum_incr( slot ))
		{
			m_highlight[slot]->set_visible( can_accept( drag_artifact, m_artifact_sources[slot] ) );
		}
	}
	else
	{
		for (t_artifact_slot slot = t_artifact_slot(0); slot < k_artifact_slot_hero_count; enum_incr( slot ))
		{
			if ( m_highlight[slot] )
				m_highlight[slot]->set_visible( false );
		}
	}
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// check if a give drop is legal
// -----------------------------------------------------------------------
bool t_army_dialog_base::can_accept( t_drag_artifact* drag, t_drag_artifact_source* dest ) const
{
	if ( dest == NULL )
		return false;

	t_artifact_slot slot = dest->get_slot();

	if (slot == k_artifact_slot_none)
		return true;

	t_hero const*     hero = dest->get_stack()->get_hero();
	t_artifact const& artifact = drag->get_artifact();
	t_artifact_slot   target_slot = get_slot( artifact.get_icon() );

	// Don't allow player to manipulate / trade away ally's artifacts
	t_creature_array const * selected_army = get_selected_army();

	assert( selected_army );

	if ( selected_army )
	{
		if ( is_restricted( selected_army ) )
		{
			if ( hero->get_artifact( slot ).get_icon() != k_artifact_none )
				return false;

			if ( target_slot == k_artifact_slot_2_handed )
			{
				if (   hero->get_artifact( k_artifact_slot_weapon ).get_icon() != k_artifact_none 
					|| hero->get_artifact( k_artifact_slot_shield ).get_icon() != k_artifact_none )
				{
					return false;
				}
			}
			else if ( slot == k_artifact_slot_shield )
			{
				t_artifact_type weapon = hero->get_artifact( k_artifact_slot_weapon ).get_icon();

				// if the target has a 2-handed weapon, don't remove shield
				if (weapon != k_artifact_none && get_slot( weapon ) == k_artifact_slot_2_handed )
					return false;
			}
		}
	}


	if (target_slot < k_artifact_slot_hero_count)
		return target_slot == slot;

	switch (target_slot)
	{
		case k_artifact_slot_2_handed:
			if (slot != k_artifact_slot_weapon)
				return false;
			break;

		case k_artifact_slot_potion:
		case k_artifact_slot_fragment:
			return false;

		case k_artifact_slot_misc:
			if (slot < k_artifact_slot_misc_1 || slot > k_artifact_slot_misc_4)
				return false;
			break;

		case k_artifact_slot_ring:
			if (slot != k_artifact_slot_left_ring && slot != k_artifact_slot_right_ring)
				return false;
			break;
	}
	return true;
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// restore an artifact to a slot
// -----------------------------------------------------------------------
void t_army_dialog_base::restore( t_drag_artifact_source* source )
{
	t_artifact        artifact = source->get_artifact();
	t_creature_stack* stack = source->get_stack();
	t_artifact_slot   slot = source->get_slot();

	if (slot != k_artifact_slot_none)
	{
		// place artifact in slot, and remove old one.
		t_hero*    hero = stack->get_hero();

		if (!hero->equip( artifact, slot ))
			hero->add( artifact );
	}
	else
	{
		stack->add_to_backpack( artifact, source->get_backpack_slot() );
	}
}

t_stack_with_backpack* t_army_dialog_base::get_selected_stack()
{
	if (m_selected_creature_array == 0)
		return 0;
	if (m_selected_slot < 0)
		return 0;
	return dynamic_cast<t_stack_with_backpack*>(&(*m_selected_creature_array)[m_selected_slot]);
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// respond to a scroll backpack down click
// -----------------------------------------------------------------------
void t_army_dialog_base::backpack_down_click( t_button* button )
{
	t_stack_with_backpack* stack = get_selected_stack();

	if (stack == 0)
		return;

	if (m_backpack_start + k_backpack_size < stack->get_backpack_count())
	{
		m_backpack_start++;
		update_artifacts();
	}
}

void t_army_dialog_base::scrollbar_move( t_scrollbar* scrollbar, int value )
{
	t_stack_with_backpack* stack = get_selected_stack();

	if (stack == 0)
		return;

    // Go up by 2s, because it's a 2x5 grid.
	m_backpack_start = (value*2);
	update_artifacts();
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// respond to a scroll backpack up click
// -----------------------------------------------------------------------
void t_army_dialog_base::backpack_up_click( t_button* button )
{
	t_stack_with_backpack* stack = get_selected_stack();

	if (stack == 0)
		return;

	if (m_backpack_start > 0)
	{
		m_backpack_start--;
		update_artifacts();
	}
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// set up all the artifact icons
// -----------------------------------------------------------------------
void t_army_dialog_base::update_artifacts()
{
	if (m_selected_creature_array == 0)
		return;

	t_creature_stack& slot = (*m_selected_creature_array)[m_selected_slot];

	if (slot.get_number() == 0)
		return;

	set_artifact_icons( slot );
	update_statistics();
}


// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// call this after the selected stack has been removed from its army
// -----------------------------------------------------------------------
void t_army_dialog_base::handle_selected_stack_removal()
{
	assert( m_selected_creature_array != 0 );
	assert( m_selected_slot >= 0 );
	assert( ( *m_selected_creature_array )[ m_selected_slot ].get_number() == 0 );

  	m_selected_window->update();
    m_selected_window->select_first();
    if (m_selected_creature_array->empty())
    {
	    if (m_adjacent_army_window && m_selected_window == m_army_window)
        {
	        m_adjacent_army_window->select_first();
	        m_adjacent_army_window->update();
        }
        else
        {
	        m_army_window->select_first();
	        m_army_window->update();
        }
    }

    // Close window if no creatures.
    if ((m_creatures->empty()) && (!m_adjacent_army || m_adjacent_army->empty()))
    {
        close_creature_windows();
        close();
    }
}

// Sort potions to bottom.
void t_army_dialog_base::hide_potions( t_button* button )
{
    m_backpack_hide_potions = !m_backpack_hide_potions;

    m_hide_potions_button->set_visible( m_backpack_hide_potions );
    m_show_potions_button->set_visible( !m_backpack_hide_potions );
    update_artifacts();
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
static t_external_string const k_text_dismiss_creature( "dismiss_creature.misc" );

void t_army_dialog_base::dismiss_creature( t_button* button )
{
    if (!(yes_no_dialog( k_text_dismiss_creature )))
        return;

	t_adventure_map * map = m_adventure_frame->get_map();
	
	t_creature_stack & stack = (*m_selected_creature_array)[ m_selected_slot ];

	t_artifact_list leftovers;

	stack.redistribute_artifacts( *m_selected_creature_array, leftovers );

    m_selected_creature_array->clear( m_selected_slot );

	if ( !leftovers.empty() )
	{
		// Create artifact pile if this is the last unit in this army...
		t_counted_ptr< t_adv_artifact_pile > pile;

		t_adv_map_point point;

		get_artifact_pile_position( point, pile );

		if ( pile == NULL )
		{
			pile = new t_adv_artifact_pile( map->is_ocean( point ) );

			pile->place( *map, point );
		}

		pile->add_artifacts( leftovers, m_selected_creature_array->get_owner(), NULL );
	}

	handle_selected_stack_removal();
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// -----------------------------------------------------------------------
bool t_army_dialog_base::cast_spell( t_spellbook_window_data const& spell_data ) const
{
	assert( get_selected_army() != 0 );
	assert( get_selected_slot() >= 0 );

	if ( is_restricted( get_selected_army() ) ) 
		return false;

	return m_adventure_frame->cast_adventure_spell( *get_selected_army(), get_selected_slot(), spell_data );
}

// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// display a hero's spells
// -----------------------------------------------------------------------
void t_army_dialog_base::spellbook_click( t_button* )
{
	if (m_selected_creature_array == 0)
		return;
	assert( m_selected_window != 0 );

	t_creature_stack& stack = (*m_selected_creature_array)[m_selected_slot];

	if (stack.get_number() == 0 || !stack.can_cast_spells())
		return;

	t_counted_ptr<t_spellbook_window> dialog;
	t_cached_grail_data_source grail_data( m_selected_creature_array->get_grail_data() );

	dialog = new t_spellbook_window( &stack, get_parent(), grail_data );
	if (!dialog->run_modal())
		return;

	if ( cast_spell( dialog->get_selected_spell() ) )
	{
		t_spell spell = dialog->get_selected_spell().spell;
		if ( spell != k_spell_town_gate && spell != k_spell_mire )
		{
			// If the caster is now gone, call the function to handle the
			// removal of the selected stack, otherwise just update the
			// displayed statistics
			if ( ( *m_selected_creature_array )[ m_selected_slot ].get_number() == 0 )
				handle_selected_stack_removal();
			else
			{
				update_artifacts();
				m_selected_window->update();
			}
		}
		else
		{
			// If the army just "town gated" away shut down this army dialog
			close_creature_windows();
			close();
		}
	}
}

/*---------------------------------------------------------------------------*\
**
** Do we treat the given army as restricted? Restricted armies do not allow
** us to drag units or artifacts
**
\*---------------------------------------------------------------------------*/
bool t_army_dialog_base::is_restricted( bool visiting_army ) const
{
	t_adventure_map & map = *m_adventure_frame->get_map();
	int player_number = map.get_player_number();

	if ( visiting_army )
	{
		return m_adjacent_army && m_adjacent_army->get_owner_number() != player_number;
	}
	else
	{
		return m_creatures && m_creatures->get_owner_number() != player_number;
	}
}

bool t_army_dialog_base::is_restricted( t_creature_array const * army ) const
{
	return is_restricted( army == m_adjacent_army );
}


/*---------------------------------------------------------------------------*\
**
** Check if we need the special allied restrictions on where creatures, heroes,
** and artifacts can be dragged. Return true if the restrictions are in place.
**
\*---------------------------------------------------------------------------*/
bool t_army_dialog_base::check_for_allied()
{
	t_adventure_map & map = *m_adventure_frame->get_map();
	int player_number = map.get_player_number();

	if (   is_restricted( true )
		|| is_restricted( false ) )
	{
		if ( m_adjacent_army_window )
		{
			setup_foreign_creature_array_window_controls( map, m_adjacent_army_window );
		}

		if ( m_army_window )
		{
			setup_foreign_creature_array_window_controls( map, m_army_window );
		}

		bool enable_up_buttons = !is_restricted( true );

		if ( m_move_up_button )
			m_move_up_button->enable( enable_up_buttons );

		if ( m_move_tombstone_up_button )
			m_move_tombstone_up_button->enable( enable_up_buttons );


		bool enable_down_buttons = !is_restricted( false );
		if ( m_move_down_button )
			m_move_down_button->enable( enable_down_buttons );

		if ( m_move_tombstone_down_button )
			m_move_tombstone_down_button->enable( enable_down_buttons );

		if ( m_move_garrison_down_button )
			m_move_garrison_down_button->enable( enable_down_buttons );

		return true;
	}
	else
	{
		// Clear handlers
		if ( m_adjacent_army_window )
		{
			clear_foreign_creature_array_window_controls( m_adjacent_army_window );
		}

		if ( m_army_window )
		{
			clear_foreign_creature_array_window_controls( m_army_window );
		}

		if ( m_move_up_button )
			m_move_up_button->enable( true );

		if ( m_move_tombstone_up_button )
			m_move_tombstone_up_button->enable( true );

		if ( m_move_down_button )
			m_move_down_button->enable( true );

		if ( m_move_tombstone_down_button )
			m_move_tombstone_down_button->enable( true );

		if ( m_move_garrison_down_button )
			m_move_garrison_down_button->enable( true );

		return false;
	}
}

void t_army_dialog_base::select_leader()
{
	if (!m_creatures->empty() || m_adjacent_army_window == 0)
		m_army_window->select_leader();
    else
  	    m_adjacent_army_window->select_leader();
}


/*---------------------------------------------------------------------------*\
**
** Army dialog for town window.
**
\*---------------------------------------------------------------------------*/
t_army_dialog_town::t_army_dialog_town( t_creature_array* creatures, t_army* adjacent_army, t_adventure_frame* adventure_frame,
		           t_creature_array* selected_creatures, t_town *town_ptr, int selected_slot )
			 : t_army_dialog_base( creatures, adjacent_army, adventure_frame, selected_slot )
{
    m_town = town_ptr;

	create_buttons();
	create_creature_windows();
	create_icons();
	create_text_boxes();
	check_for_allied();

	// select first creature
	if (selected_slot < 0)
    {
		select_leader();
    }
	else
    {
        if (creatures == selected_creatures)
    		m_army_window->select_creature( selected_slot );
        else
    	    m_adjacent_army_window->select_creature( selected_slot );
    }
}



// -----------------------------------------------------------------------
// dialog to manipulate one or two armies
// If the last creature in the army is dismissed, find a place to put the 
// resulting artifact pile
// -----------------------------------------------------------------------
void t_army_dialog_town::get_artifact_pile_position( t_adv_map_point & point, t_counted_ptr< t_adv_artifact_pile > & pile )
{
	t_adventure_map * map = m_town->get_map();

	if ( m_selected_creature_array == m_adjacent_army )
	{
		if ( m_adjacent_army->is_on_map())
		{
			point = m_adjacent_army->get_position();
	
			// Look for existing pile
			t_adv_artifact_pile::is_good_creation_point( point, pile, map );
			return;
		}
	}

	// Scan nearby points for good location
	t_adv_artifact_pile::scan_for_good_creation_point( *m_town, *m_town, point, pile );
}

